package com.example.demospringmybatisplus.aspect;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.example.demospringmybatisplus.annotation.SecureInvoke;
import com.example.demospringmybatisplus.vo.SecureInvokeVo;
import com.example.demospringmybatisplus.entity.SecureInvokeRecord;
import com.example.demospringmybatisplus.service.SecureInvokeService;
import com.example.demospringmybatisplus.utils.JsonUtils;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 安全执行切面
 *
 * @author hauhua
 * @DATE 2025/1/21
 */
@Slf4j
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE + 1)// 确保最先执行
@Component
public class SecureInvokeAspect {
    @Autowired
    private SecureInvokeService secureInvokeService;

    @Around("@annotation(secureInvoke)")
    public Object around(ProceedingJoinPoint joinPoint, SecureInvoke secureInvoke) throws Throwable {
        boolean async = secureInvoke.async();
        boolean inTransaction = TransactionSynchronizationManager.isActualTransactionActive();
        // 非事务状态，直接执行，不做任何处理
        if (!inTransaction) {
            return joinPoint.proceed();
        }
        // 如果是事务状态，保证保存到本地消息表中的数据和保存在数据库中数据，在同一个事务内
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        List<String> parameters = Stream.of(method.getParameterTypes())
            .map(Class::getName)
            .collect(Collectors.toList());
        SecureInvokeVo dto = SecureInvokeVo.builder()
            .args(JsonUtils.toStr(joinPoint.getArgs()))
            .className(method.getDeclaringClass().getName())
            .methodName(method.getName())
            .parameterTypes(JsonUtils.toStr(parameters))
            .build();
        SecureInvokeRecord record = SecureInvokeRecord.builder()
            .secureInvokeVo(dto)
            .maxRetryTimes(secureInvoke.maxRetryTimes())
            .nextRetryTime(DateUtil.offsetMinute(new Date(), (int) SecureInvokeService.RETRY_INTERVAL_MINUTES))
            .createTime(new Date())
            .updateTime(new Date())
            .build();
        secureInvokeService.invoke(record, async);
        return null;
    }
}
